package com.yzi.core.command
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	public class MacroCommand extends EventDispatcher implements ICommand
	{
		private var m_undoCommand:ICommand;
		private var m_redoCommand:ICommand;
		
		private var m_maxStackSize:int;
		
		private var m_redoCommandList:Vector.<ICommand>;
		private var m_undoCommandList:Vector.<ICommand>;
		
		public function MacroCommand($maxStackSize:uint = 20)
		{
			m_maxStackSize = $maxStackSize;
			
			init();
		}
		
		private function init(): void
		{
			m_redoCommandList = new Vector.<ICommand>();
			m_undoCommandList = new Vector.<ICommand>();
		}
		
		public function Execute():void
		{
		}
		
		public function Append($command:ICommand): void
		{
			if($command != this) m_undoCommandList.push($command);
			if(m_undoCommandList.length > m_maxStackSize) m_undoCommandList.shift();
			m_redoCommandList.length = 0;
			
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		public function Remove($command:ICommand): void
		{
			RemoveAt(GetIndex($command));
		}
		
		public function RemoveAt($index:int): void
		{
			if($index < 0 || $index > m_undoCommandList.length) throw new Error("Out od Range");
			m_undoCommandList.splice($index, 1);
			
			m_redoCommandList.length = 0;
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		public function Reset(): void
		{
			init();
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		public function Undo(): void
		{
			if(IsUndoable() && (m_undoCommandList.length <= m_maxStackSize)) {
				m_undoCommand = m_undoCommandList.pop();
				m_redoCommandList.push(m_undoCommand);
				m_undoCommand.Execute();
				dispatchEvent(new Event(Event.CHANGE));
			}
		}
		
		public function Redo(): void
		{
			if(IsRedoable() && (m_redoCommandList.length <= m_maxStackSize)) {
				m_redoCommand = m_redoCommandList.pop();
				m_undoCommandList.push(m_redoCommand);
				m_redoCommand.Execute();
				dispatchEvent(new Event(Event.CHANGE));
			}
		}
		
		public function GetAt($index:int): ICommand
		{
			if($index < 0 || $index >= m_undoCommandList.length) throw new Error("Out of Range");
			return m_undoCommandList[$index];
		}
		
		public function GetIndex($command:ICommand): int { return m_undoCommandList.indexOf($command); }
		
		public function GetLength(): int { return m_undoCommandList.length; }
		
		public function GetCapacity(): int { return m_maxStackSize; }
		
		public function IsUndoable(): Boolean { return (m_undoCommandList.length > 0); }
		
		public function IsRedoable(): Boolean { return (m_redoCommandList.length > 0); }
	}
}